# -*- coding: utf-8 -*-
"""
------------------------------------------------------------------------------
    File Name:  chinese_character_split
    Author   :  wanwei1029
    Date     :  2018/2/10
    Desc     :  通过投影法进行汉字切割，目前问题主要有：
                1：左右型汉字被分开。
                2：字距过小时，多个字可能切割到一起，可以按字宽重新切割。
------------------------------------------------------------------------------
"""
import os
import sys
import logging
import cv2
import numpy as np

logger = logging.getLogger("chinese_character_split")
formatter = logging.Formatter('%(asctime)s %(levelname) -8s: %(message)s')
console_handler = logging.StreamHandler(sys.stdout)
console_handler.formatter = formatter
logger.addHandler(console_handler)
logger.setLevel(logging.INFO)

DEFAULT_SAVE_PATH = "./chars/"
SPLIT_COLOR = (0, 0, 255)
SPLIT_MODE_SHOW = "show"
SPLIT_MODE_SPLIT = "split"


def split(img, split_mode=SPLIT_MODE_SHOW, save_path=DEFAULT_SAVE_PATH):
    """
    切割图片
    :param img:cv2读入的三维图像。
    :param split_mode:模拟切割和真实切割，默认虚拟
    :param save_path:切割的字符图像保存的地址，默认当前目录下chars目录
    :return:void
    """
    gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    threshold_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
    horizontal_sum = np.sum(threshold_img, axis=1)
    horizontal_peak_list = extract_array_peak(horizontal_sum)
    vertical_peak_lists = []
    for horizontal_peak in horizontal_peak_list:
        line_item = threshold_img[horizontal_peak[0]:horizontal_peak[1], :]
        vertical_sum = np.sum(line_item, axis=0)
        vertical_peak_list = extract_array_peak(vertical_sum, min_val=100, min_range=1)
        vertical_peak_list = process_multi_character(vertical_peak_list)
        vertical_peak_lists.append(vertical_peak_list)
    # 切割
    for i, horizontal_peak in enumerate(horizontal_peak_list):
        for j, vertical_peak in enumerate(vertical_peak_lists[i]):
            if split_mode == SPLIT_MODE_SPLIT:
                char_img = img[horizontal_peak[0]:horizontal_peak[1], vertical_peak[0]:vertical_peak[1]]
                char_save_path = save_path+str(i)+"_"+str(j)+".jpg"
                logger.debug("save char with path :{0}".format(char_save_path))
                cv2.imwrite(char_save_path, char_img)
            else:
                pt1 = (vertical_peak[0], horizontal_peak[0])
                pt2 = (vertical_peak[1], horizontal_peak[1])
                cv2.rectangle(img, pt1, pt2, SPLIT_COLOR)


def process_multi_character(vertical_peak_list):
    """
    处理间距过小多个汉字连在一起的情况，按平均字宽进行切割。
    :param vertical_peak_list:峰距数组，
    :return:处理过后的峰距数组，拆分了连字情况。
    """
    vertical_peak_widths = []
    for vertical_peak in vertical_peak_list:
        vertical_peak_widths.append(vertical_peak[1] - vertical_peak[0] + 1)
    most_width = get_most_item(np.asarray(vertical_peak_widths))
    logger.debug("most width is {0}".format(most_width))
    new_peak_list = []
    for i, vertical_peak in enumerate(vertical_peak_list):
        char_num = int(round(vertical_peak_widths[i]/most_width, 0))
        if char_num > 1:
            for j in range(char_num):
                start = vertical_peak[0]+most_width*j
                end = vertical_peak[0]+most_width*(j+1)-1
                if j == char_num -1:
                    end = vertical_peak[1]
                new_peak_list.append([start, end])
        else:
            new_peak_list.append([vertical_peak[0], vertical_peak[1]])
    return new_peak_list


def extract_array_peak(org_array, min_val=1, min_range=2):
    """
    取一维数组的峰距。
    :param org_array:一维数组
    :param min_val:峰值最小值，大于等于此值的才算峰
    :param min_range:最小峰距，峰距小于此值的不返回
    :return:二维数组，每一个元素包括峰距的起点和终点。
    """
    start_idx = -1
    end_idx = -1
    peak_list = []
    for i, item in enumerate(org_array):
        if item < min_val and start_idx == -1:
            pass
        elif item >= min_val and start_idx == -1:
            start_idx = i
        elif item < min_val or (start_idx != -1 and i == len(org_array)-1):
            end_idx = i-1
            if start_idx != -1 and i == len(org_array)-1:
                end_idx = i
            if end_idx - start_idx >= min_range:
                peak_list.append([start_idx, end_idx])
            start_idx = -1
            end_idx = -1
    return peak_list


def get_most_item(np_arrays, min_val=1, max_val=100):
    """
    计算数组中哪个元素出现的频率最高。如果频率相同取值较大的那个
    :param np_arrays: 数组类型必须是ndarray，不能是普通的list
    :param min_val: 比较排除的最小值，不包括
    :param max_val: 比较排除的最大值，不包括
    :return: 出现频率最高的值。
    """
    sorted_array = sorted([(np.sum(np_arrays == item), item) for item in np_arrays if (min_val < item < max_val)])
    logger.debug("sort array : {0}".format(sorted_array))
    return sorted_array[-1][1]


if __name__ == '__main__':
    test_method = "split"
    logger.setLevel(logging.DEBUG)
    if test_method == "get_most_item":
        test_array = [3, 2, 4, 5, 2, 4, 4, 6, 8, 1, 4, 8, 2, 1, 9, 2]
        print(get_most_item(np.asarray(test_array)))
    elif test_method == "process_multi_character":
        test_array = [[3, 5], [7, 9], [15, 19], [22, 35]]
        print(process_multi_character(test_array))
    elif test_method == "extract_array_peak":
        test_array = [0,0,5,0,4,3,6,0,9,3,3,3,0,0,0,0,7,6,7,7,0,0]
        print(extract_array_peak(test_array))
    elif test_method == "split":
        test_img = cv2.imread(os.path.join("./images", "test01.jpg"))
        # split(test_img, split_mode=SPLIT_MODE_SHOW)
        split(test_img)
        cv2.imshow("split image", test_img)
        cv2.waitKey(0)